#include "utilities.h"

void *acceptChildThr(void *ptr)
{
	int* sockets = (int*) ptr;
	int connected = sockets[1];
	if(printFlag==1)
	{
	printf("Connection Accepted\n");
	}
	char buf[512];
	hello_msg t1;
	msg_header h1;
	int bytes_recieved;
	bytes_recieved = read(connected,buf,sizeof buf);
	if(bytes_recieved==0)
	{
		if(printFlag==1)
		{
			printf("Error in message recieved\n");
		}
		close(connected);
		pthread_exit(NULL);
	}
	struct timeval readtime;
	gettimeofday(&readtime,NULL);
	fillMsgHdrStructure(h1, buf);
	if(h1.msgtype==HLLO)
	{
		fillMsgHdrStructure(t1.header, buf);
		memcpy(&(t1.hostport),&(buf[27]),sizeof(uint16_t));
		memcpy(t1.hostname,&(buf[29]),myHello.header.datalength - sizeof(uint16_t));
			
		listBuffStruct lbtemp;
		lbtemp.logtype = 0;
		memset(lbtemp.listBuff, '\0', bytes_recieved);
		memcpy(lbtemp.listBuff, buf, bytes_recieved);
		lbtemp.datalength = bytes_recieved;
		lbtemp.portNumber = t1.hostport;
		write_to_log(lbtemp,lbtemp.logtype,readtime);
		if(printFlag==1)
		{
			printf("Message Type is %x\nMessage UOID is ",t1.header.msgtype);
		}
		for(int i=0;i<20;i++)
		{
			uint8_t x = t1.header.uoid[i];
			if(printFlag==1)
			{
				printf("%02x",x);
			}
		}
		if(printFlag==1)
		{
			printf("\nMessage TTL is %d\nMessage Length is %d\n",t1.header.timetolive,t1.header.datalength);
			printf("The connecting beacon node portnumber : %d and hostname : %s \n",t1.hostport, t1.hostname);
		}
		fillMsgHdrBuffer(myHello.header, buf);
		memcpy(&(buf[27]),&(myHello.hostport),sizeof(uint16_t));
		memcpy(&(buf[29]),myHello.hostname,myHello.header.datalength - sizeof(uint16_t));
		pthread_mutex_lock(&maplock);
		if(myMap.find(t1.hostport)->second.connectflag==0)
		{
			beaconmap temp1;
			temp1.socknumber = connected;
			temp1.connectflag = 1;
			myMap[t1.hostport] = temp1;
			pthread_mutex_unlock(&maplock);
			struct timeval writetime;
			gettimeofday(&writetime,NULL);
			write(connected,buf,27+sizeof(myHello.hostport)+strlen(myHello.hostname));
			listBuffStruct lbtemp;
			lbtemp.logtype = 1;
			memset(lbtemp.listBuff, '\0', sizeof buf);
			memcpy(lbtemp.listBuff, buf,sizeof buf);
			lbtemp.datalength = 27+sizeof(myHello.hostport)+strlen(myHello.hostname);
			lbtemp.portNumber = t1.hostport;
			write_to_log(lbtemp,lbtemp.logtype,writetime);
		}
		else
		{
			if(t1.hostport<port_recieve)
			{
				close(connected);
				pthread_exit(NULL);
			}
			else if(t1.hostport>port_recieve)
			{
				close(myMap.find(t1.hostport)->second.socknumber);
				beaconmap temp1;
				temp1.socknumber = connected;
				temp1.connectflag = 1;
				myMap[t1.hostport] = temp1;
				pthread_mutex_unlock(&maplock);
				struct timeval writetime;
				gettimeofday(&writetime,NULL);
				write(connected,buf,27+sizeof(myHello.hostport)+strlen(myHello.hostname));
				listBuffStruct lbtemp;
				lbtemp.logtype = 1;
				memset(lbtemp.listBuff, '\0', sizeof buf);
				memcpy(lbtemp.listBuff, buf,sizeof buf);
				lbtemp.datalength = 27+sizeof(myHello.hostport)+strlen(myHello.hostname);
				lbtemp.portNumber = t1.hostport;
				write_to_log(lbtemp,lbtemp.logtype,writetime);
			}
			else if(t1.hostport==port_recieve)
			{
				if(strcmp(t1.hostname,hostname)<0)
				{
					close(connected);
					pthread_exit(NULL);
				}
				else
				{
					close(myMap.find(t1.hostport)->second.socknumber);
					beaconmap temp1;
					temp1.socknumber = connected;
					temp1.connectflag = 1;
					myMap[t1.hostport] = temp1;
					pthread_mutex_unlock(&maplock);
					struct timeval writetime;
					gettimeofday(&writetime,NULL);
					write(connected,buf,27+sizeof(myHello.hostport)+strlen(myHello.hostname));
					listBuffStruct lbtemp;
					lbtemp.logtype = 1;
					memset(lbtemp.listBuff, '\0', sizeof buf);
					memcpy(lbtemp.listBuff, buf,sizeof buf);
					lbtemp.datalength = 27+sizeof(myHello.hostport)+strlen(myHello.hostname);
					lbtemp.portNumber = t1.hostport;
					write_to_log(lbtemp,lbtemp.logtype,writetime);
				}
			}
		}
		
		pthread_t readThr;
		pthread_t writeThr;
		
		allTimers tempTimer;
		tempTimer.keepAliveTime = 0;
		tempTimer.joinTime = 0;
		myTimers[t1.hostport] = tempTimer;
		writeListData listDataStr;
		list<listBuffStruct> writeList; 
		writeStruct wrStruct;
		wrStruct.portNumber = t1.hostport;
		wrStruct.sock = connected;
		listDataStr.response = writeList;
		listDataStr.dieflag = false;
		memset(listDataStr.hostname,'\0',512);
		memcpy(listDataStr.hostname,t1.hostname,strlen(t1.hostname));
		pthread_mutex_lock(&writeMapMutex);
		writeMap[t1.hostport] = listDataStr;
		if(printFlag==1)
		{
			cout<<"Hello finished"<<t1.hostport<<endl;
		}
		pthread_mutex_unlock(&writeMapMutex);
		pthread_create(&readThr, NULL, readfun, (void *)&wrStruct);
		pthread_create(&writeThr, NULL, writefun, (void *)&wrStruct);
	}
	if(h1.msgtype==JNRQ)
	{
		if(printFlag==1)
		{
			cout<<"join request"<<endl;
		}
		joinFlag = false;
		listBuffStruct lbtemp;
		gettimeofday(&readtime,NULL);
		memset(&lbtemp.listBuff, '\0', 512);
		memcpy(&lbtemp.listBuff, buf, 27);
		memcpy(&(lbtemp.listBuff[27]), &(buf[27]), h1.datalength);
		lbtemp.datalength = 27+h1.datalength;
		join_msg tempJoinMsg;
		memcpy(&(tempJoinMsg.hostport), &(buf[31]), sizeof(uint16_t));
		lbtemp.portNumber = tempJoinMsg.hostport;
		write_to_log(lbtemp,0,readtime);
		joinRequestMap[lbtemp.portNumber] = true;
		pthread_mutex_lock(&eventQueueMutex);
		eventQueue.push_back(lbtemp);
		pthread_mutex_unlock(&eventQueueMutex);
		beaconmap temp1;
		temp1.socknumber = connected;
		pthread_mutex_lock(&maplock);
		myMap[lbtemp.portNumber] = temp1;
		pthread_mutex_unlock(&maplock);
	}
	pthread_exit(NULL);
}

void* accept_f(void* ptr)
{
	int sock,connected;
	struct sockaddr_in server_addr,client_addr;    
	socklen_t sin_size;
	int t = 1;
	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
	{
		perror("Socket");
		exit(1);
	}
	
	if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&t,sizeof(int)) == -1) 
	{
		perror("Setsockopt");
		exit(1);
	}
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port_recieve);     
	server_addr.sin_addr.s_addr = INADDR_ANY; 
	memset(&(server_addr.sin_zero), 0, sizeof server_addr.sin_zero);
	
	//bind the socket to local port
	if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) 
	{
		if(printFlag==1)
		{
			printf("Bad Port Number\n");
		}
		exit(1);
	}
	//listening connections from the users
	
	if (listen(sock, 5) == -1) 
	{
		perror("Listen");
		exit(1);
	}
	
	sin_size = sizeof(struct sockaddr_in);
	//Particular user connects to the library
	/* Check the flag once in a while to see when to quit.  */
	globalAcceptSock = sock;
	while (!dieChild)
	{
		if(printFlag==1)
		{
			printf("Waiting for connection\n");
		}
		connected = accept(sock, (struct sockaddr *)&client_addr,&sin_size);//accept connections
		
		if(serverShutdown == true)
		{
			if(printFlag==1)
			{
				cout<<"accept shutdown"<<endl;
			}
			break;
		}
		if (connected == -1) 
		{
			if(printFlag==1)
			{
				cout<<"probs"<<endl;
			}
			continue;
		}
		int sockets[2] = {sock, connected};
		pthread_t childThread;
		pthread_create(&childThread, NULL, acceptChildThr, (void*)sockets);
	}
	close(sock);
	if(printFlag==1)
	{
		cout<<"Out of accept"<<endl;
	}
	pthread_exit(NULL);
}
